package de.uplinkgmbh.lms.client;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.axone.exception.Assert;
import de.axone.wash.Wash;
import de.axone.wash.Wash.NotFoundException;
import de.axone.wash.Wash.WashException;
import de.axone.wash.Wash.WrongTypeException;
import de.axone.wash.client.WashClient;
import de.axone.wash.client.WashClient.ClientException;

public class LmsClient {
	
	private static final Logger log = LoggerFactory.getLogger( LmsClient.class );
	
	private final WashClient wc;
	private final String application;

	public LmsClient( URL url, String application ){
		
		Assert.notNull( url, "url" );
		Assert.notNull( application, "application" );
		
		this.wc = new WashClient( url );
		this.application = application;
	}
	
	public LmsToken login( String loginname, String passwd ) throws LmsException {
		
		Assert.notNull( loginname, "loginname" );
		Assert.notNull( passwd, "passwd" );
		
		LoginWash lw = new LoginWash( loginname, passwd, application );
		
		Wash res = call( "login", "login", lw );
		try {
			checkStatus( res, "Cannot log in" );
			
			String token = getString( res, "LMSTOKEN" );
			
			log.info( "Logged in: " + loginname );
			
			return new LmsToken( token );
		} catch( LmsException e ){
			return null;
		}
	}
	
	public void logout( LmsToken token ) throws LmsException {
		
		Assert.notNull( token, "token" );
		
		LogoutWash lw = new LogoutWash( token );
		
		Wash res = call( "login", "logout", lw );
		checkStatus( res, "Cannot log out" );
		
		long time = Long.parseLong( getString( res, "REASON" ) );
		
		log.info( "Log out after " + time/1000 + " s" );
	}
	
	public boolean isAllowed( LmsToken token,
			String state, String action, String target ) throws LmsException {
		
		Assert.notNull( token, "token" );
		if( state == null ) state = "";
		if( action == null ) action = "";
		if( target == null ) target = "";

		StateActionTargetWash lw = new StateActionTargetWash( token, state, action, target );

		Wash res = null;

		res = call( "action", "isAllowed", lw );
		checkStatus( res, "Wrong token" );
		
		return getBoolean( res, "PERMISSION" );
	}
	
	public List<LmsAction> getWildcardActions( LmsToken token,
			String state, String action, String target ) throws LmsException {
		
		Assert.notNull( token, "token" );

		StateActionTargetWash lw = new StateActionTargetWash( token, state, action, target );

		Wash res = null;
		
		res = call( "action", "getWildcardActions", lw );
		checkStatus( res, "Wrong token" );
		
		int len = (int)(long)getLong( res, "SIZE" );
		
		ArrayList<LmsAction> result = new ArrayList<LmsAction>( len );
		
		for( int i=0; i<len ;i++ ){
			
			LmsAction lmsAction = new LmsAction();
			lmsAction.read( res, i );
			
			result.add( i, lmsAction );
		}
		
		return result;
	}
	
	// --- PRIVATE ---
	private Wash call( String service, String op, Wash wash ) throws LmsException {
		
		try {
			return wc.call( service, op, wash );
		} catch (ClientException e) {
			throw new LmsException( "Cannot connect to LMS via " + wc.getUrl(), e );
		} catch (WashException e) {
			throw new LmsWashException( "Error in wash", wash, e );
		}
	}
	
	private static void checkStatus( Wash res, String message ) throws LmsException {
		
		if( ! getBoolean( res, "STATUS" ) ){
			throw new LmsWashException( message, res );
		}
	}
	
	private static String getString( Wash res, String name ) throws LmsException {
		
		try {
			return res.getString( name );
		// Generic Server/Wash error
		} catch( NotFoundException e ) {
			throw new LmsWashException( "Result is incomplete (missing " + name + ")", res, e );
		} catch( WrongTypeException e ) {
			throw new LmsWashException( "Some error in result", res, e );
		}
	}
	
	private static boolean getBoolean( Wash res, String name ) throws LmsException {
		
		try {
			return res.getBoolean( name );
		// Generic Server/Wash error
		} catch( NotFoundException e ) {
			throw new LmsWashException( "Result is incomplete (missing " + name + ")", res, e );
		} catch( WrongTypeException e ) {
			throw new LmsWashException( "Some error in result", res, e );
		}
	}
	
	private static Long getLong( Wash res, String name ) throws LmsException {
		
		try {
			return res.getLong( name );
		// Generic Server/Wash error
		} catch( NotFoundException e ) {
			throw new LmsWashException( "Result is incomplete (missing " + name + ")", res, e );
		} catch( WrongTypeException e ) {
			throw new LmsWashException( "Some error in result", res, e );
		}
	}
	
	public static class LmsLoginException extends LmsWashException {

		public LmsLoginException( String message, Wash wash ) {
			super( message, wash );
		}
		
	}
	
	public static class LmsAction {
		public String name;
		public long sort;
		public String target;
		public String action;
		public String state;
		public String rule;
		public void read( Wash wash, int no ) throws LmsException{
			
			name = getString( wash, "Action-" + no + ".NAME" );
			sort = getLong( wash, "Action-" + no + ".SORT" );
			target = getString( wash, "Action-" + no + ".TARGET" );
			action = getString( wash, "Action-" + no + ".ACTION" );
			state = getString( wash, "Action-" + no + ".STATE" );
			rule = getString( wash, "Action-" + no + ".RULE" );
		}
		@Override
		public String toString(){
			
			return "(" + sort + ") " + name + ": " + target + "/" + action + "/" + state + "=" + rule;
		}
	}
}
